/*
 * procfs1.c
 */

#include <linux/kernel.h> 
#include <linux/module.h> 
#include <linux/proc_fs.h> 
#include <linux/uaccess.h> 
#include <linux/version.h>
#include <linux/string.h> 

#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 6, 0) 
#define HAVE_PROC_OPS 
#endif 

#define procfs_name "helloworld" 

static struct proc_dir_entry* our_proc_file;
#define Data_Len 32
static unsigned data[Data_Len];

static ssize_t procfile_read(struct file* fp, char __user* buffer,
    size_t length, loff_t* offset)
{
    char s[13] = "HelloWorld!\n";
    int len = sizeof(s);
    ssize_t ret = len;
    int remain_len = len - *offset;
    pr_info("11 copy to user, len=%ld, off=%lld\n", length, *offset);

    if (remain_len > 0) {
        if (remain_len >= length)
            len = length;
        else
            len = remain_len;

        pr_info("12 real read, len=%d\n", len);
        if (copy_to_user(buffer, s + *offset, len) == 0) {
            ret = len;
            *offset += len;
            pr_info("procfile read %s\n", fp->f_path.dentry->d_name.name);
        }
    }
    else {
        pr_info("copy_to_user failed, remain_len=%d\n", remain_len);
        ret = 0;
    }

    pr_info("ret=%ld\n", ret);
    return ret;
}

#ifdef HAVE_PROC_OPS 
static const struct proc_ops proc_file_fops = {
    .proc_read = procfile_read,
};
#else 
static const struct file_operations proc_file_fops = {
    .read = procfile_read,
};
#endif 

static int __init procfs1_init(void)
{
    // 初始化数据 data
    int i;
    for (i = 0;i < Data_Len;i++) {
        data[i] = i % ('z' - '0') + '0';
    }

    our_proc_file = proc_create(procfs_name, 0644, NULL, &proc_file_fops);
    if (NULL == our_proc_file) {
        pr_alert("Error:Could not initialize /proc/%s\n", procfs_name);
        return -ENOMEM;
    }

    pr_info("/proc/%s created\n", procfs_name);
    return 0;
}

static void __exit procfs1_exit(void)
{
    proc_remove(our_proc_file);
    pr_info("/proc/%s removed\n", procfs_name);
}

module_init(procfs1_init);
module_exit(procfs1_exit);

MODULE_LICENSE("GPL");